﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

namespace SmlEngine.Timers
{
    public sealed class GameTimer : ITimer<float, GameTimer>
    {
        #region Fields

        private float elapsedTime; //The amount of time (in milliseconds) that has elapsed since the last tick event.

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the time, in milliseconds, between two Tick events.
        /// </summary>
        public float Interval { get; set; }

        // <summary>
        /// Gets or sets the the total number of elapsed milliseconds since the timer was enabled.
        /// </summary>
        public float TotalElapsed { get; private set; }

        /// <summary>
        /// Gets or sets the whether or not the timer will update and raise Tick events.
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Gets or sets the whether or not the timer will repeat after it has raised one Tick event.
        /// </summary>
        public bool Repeats { get; set; }

        /// <summary>
        /// Gets the total number of Tick events fired since the timer was enabled.
        /// </summary>
        public int RepeatCount { get; private set; }

        /// <summary>
        /// If the timer is repeating, sets the number 
        /// of times to continue to repeat. -1 is infinite.
        /// </summary>
        public int RepeatsRemaining { get; set; }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Gets or sets the methods raised when the interval elapses
        /// </summary>
        public Action<GameTimer> Tick { get; set; }

        #endregion

        #region Constructors

        internal GameTimer() { }

        /// <summary>
        /// Constructs a new GameTimer from the specified interval. 
        /// Note the GameTimer begins disabled.
        /// </summary>
        /// <param name="interval">The number of frames between Tick events.</param>
        /// <param name="tick">The delegate to call when the interval has elapsed.</param>
        public GameTimer(float interval, Action<GameTimer> tick)
            : this(interval, tick, false) { }

        /// <summary>
        /// Constructs a new GameTimer from the specified interval, 
        /// and whether the GameTimer is enabled or disabled.
        /// </summary>
        /// <param name="interval">The number of frames between Tick events.</param>
        /// <param name="tick">The delegate to call when the interval has elapsed.</param>
        /// <param name="enabled">Whether or not the GameTimer is enabled.</param>
        public GameTimer(float interval, Action<GameTimer> tick, bool enabled)
            : this(interval, tick, enabled, true) { }

        /// <summary>
        /// Constructs a new GameTimer from the specified interval, whether the 
        /// GameTimer is enabled or disabled, and whether the GameTimer repeats.
        /// </summary>
        /// <param name="interval">The number of frames between Tick events.</param>
        /// <param name="tick">The delegate to call when the interval has elapsed.</param>
        /// <param name="enabled">Whether or not the GameTimer is enabled.</param>
        /// <param name="repeats">Whether or not the GameTimer repeats.</param>
        public GameTimer(float interval, Action<GameTimer> tick, bool enabled, bool repeats)
            : this(interval, tick, enabled, repeats, -1) { }

        /// <summary>
        /// Constructs a new GameTimer from the specified interval, whether the 
        /// GameTimer is enabled or disabled, and whether the GameTimer repeats.
        /// </summary>
        /// <param name="interval">The number of frames between Tick events.</param>
        /// <param name="tick">The delegate to call when the interval has elapsed.</param>
        /// <param name="enabled">Whether or not the GameTimer is enabled.</param>
        /// <param name="repeats">Whether or not the GameTimer repeats.</param>
        /// <param name="repeatsRemaining">
        /// The number of times to repeat 
        /// before disabling the GameTimer.
        /// </param>
        public GameTimer(float interval, Action<GameTimer> tick, bool enabled, bool repeats, int repeatsRemaining)
        {
            Interval = interval;
            Tick = tick;
            Enabled = enabled;
            Repeats = repeats;
            RepeatsRemaining = repeatsRemaining;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Updates the GameTimer, incrementing the timer by the elapsed GameTime if enabled, 
        /// and raising the Tick event if the elapsed time is equal to the interval.
        /// </summary>
        public void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                elapsedTime += gameTime.GetElapsedMilliseconds();

                while (elapsedTime >= Interval)
                {
                    Tick(this);

                    elapsedTime -= Interval;

                    RepeatCount++;

                    Enabled = Repeats;

                    if (RepeatsRemaining > 0)
                        RepeatsRemaining--;
                    else if (RepeatsRemaining == 0)
                        Enabled = false;
                }
            }
        }

        /// <summary>
        /// Starts the GameTimer by setting Enabled to true.
        /// </summary>
        public void Start()
        {
            Enabled = true;
        }

        /// <summary>
        /// Starts the GameTimer by setting Enabled to true, 
        /// and optionally resets the timer.
        /// </summary>
        /// <param name="reset">Whether or not to reset the timer to 0.</param>
        public void Start(bool reset)
        {
            Enabled = true;

            if (reset)
                Reset();
        }

        /// <summary>
        /// Starts the GameTimer by setting Enabled 
        /// to true, and changes the interval .
        /// </summary>
        /// <param name="interval">The new interval for the GameTimer.</param>
        public void Start(float interval)
        {
            Enabled = true;
            Interval = interval;
        }

        /// <summary>
        /// Starts the GameTimer by setting Enabled to true, 
        /// changing the interval, and optionally resetting the timer.
        /// </summary>
        /// <param name="interval">The new interval for the GameTimer.</param>
        /// <param name="reset">Whether or not to reset the timer to 0.</param>
        public void Start(float interval, bool reset)
        {
            Enabled = true;
            Interval = interval;

            if (reset)
                Reset();
        }

        /// <summary>
        /// Stops the GameTimer by setting Enabled to false.
        /// </summary>
        public void Stop()
        {
            Enabled = false;
        }

        /// <summary>
        /// Resets the timer to 0.
        /// </summary>
        public void Reset()
        {
            elapsedTime = 0;
            TotalElapsed = 0;
            RepeatCount = 0;
        }

        #endregion
    }
}
